package com.course.redis.service;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.course.common.enums.RedisKeyConstant;
import com.course.common.model.vo.NearMeUserVo;
import com.course.common.model.vo.ShortUserInfo;
import com.course.common.model.vo.SignInUserInfo;
import com.course.common.util.AssertUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @PROJECT_NAME: course
 * @DESCRIPTION: Redis应用之附近的人-GEO
 * @Author: 涂玄武
 * @DATE: 2021/6/28 16:17
 */
@Service
public class NearMeService {

    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private UserService userService;

    /**
     * 更新用户坐标
     *
     * @param userInfo 登录用户信息
     * @param lon      经度
     * @param lat      纬度
     */
    public void updateUserLocation(SignInUserInfo userInfo, Float lon, Float lat) {
        //获取 key user:location
        String key = RedisKeyConstant.user_location.getKey();
        //将用户地理位置信息存入Redis
        RedisGeoCommands.GeoLocation geoLocation = new RedisGeoCommands
                .GeoLocation(userInfo.getId(), new Point(lon, lat));
        redisTemplate.opsForGeo().add(key, geoLocation);
    }

    /**
     * 获取附近的人
     *
     * @param signInUserInfo 用户登录信息
     * @param radius         半径，默认 1000m
     * @param lon            经度
     * @param lat            纬度
     * @return
     */
    public List<NearMeUserVo> findNearMe(SignInUserInfo signInUserInfo,
                                         Integer radius,
                                         Float lon, Float lat) {
        //用户ID
        Integer userId = signInUserInfo.getId();
        //处理半径，默认 1000m
        if (Objects.isNull(radius)) {
            radius = 1000;
        }
        //获取key
        String key = RedisKeyConstant.user_location.getKey();
        //获取用户经纬度
        Point point = null;
        if (Objects.isNull(lon) || Objects.isNull(lat)) {
            //如果经纬度没传，那么从Redis中获取
            List<Point> points = redisTemplate.opsForGeo().position(key, userId);
            AssertUtil.isTrue(CollectionUtils.isEmpty(points), "获取经纬度失败");
            point = points.get(0);
        } else {
            point = new Point(lon, lat);
        }
        //初始化距离对象，单位 m
        Distance distance = new Distance(radius, RedisGeoCommands.DistanceUnit.METERS);
        //以用户经纬度为圆心，范围 1000m
        Circle circle = new Circle(point, distance);
        //初始化Geo命令参数对象
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        //附近的人限制20，包含距离，按由近到远排序
        args.limit(20).includeDistance().sortAscending();
        //获取附近的人 GeoLocation 信息
        GeoResults<RedisGeoCommands.GeoLocation> geoResults = redisTemplate.opsForGeo().radius(key, circle, args);
        //构建有序Map
        Map<Integer,NearMeUserVo> nearMeUserVoMap = Maps.newLinkedHashMap();
        //完善用户头像昵称信息
        geoResults.forEach(result -> {
            RedisGeoCommands.GeoLocation<Integer> geoLocation = result.getContent();
            //初始化Vo对象
            NearMeUserVo nearMeUserVo = new NearMeUserVo();
            nearMeUserVo.setId(geoLocation.getName());
            //获取距离
            Double dist = result.getDistance().getValue();
            //四舍五入精确到小数点后2位，方便客户端显示
            String distanceStr = NumberUtil.round(dist,2).toString() + "m";
            nearMeUserVo.setDistance(distanceStr);
            nearMeUserVoMap.put(geoLocation.getName(),nearMeUserVo);
        });
        //获取附近的人的信息(根据user服务接口获取)
        Integer[] userIds = nearMeUserVoMap.keySet().toArray(new Integer[]{});
        List<ShortUserInfo> shortUserInfos = userService.findByIds(StrUtil.join(",",userIds));
        //完善昵称头像信息
        shortUserInfos.forEach(shortUserInfo -> {
            NearMeUserVo nearMeUserVo = nearMeUserVoMap.get(shortUserInfo.getId());
            nearMeUserVo.setNickname(shortUserInfo.getNickname());
            nearMeUserVo.setAvatarUrl(shortUserInfo.getAvatarUrl());
        });
        return Lists.newArrayList(nearMeUserVoMap.values());
    }

}
